// jXHR.js (JSON-P XHR)
// v0.1 (c) Kyle Simpson
// MIT License
//see: http://mulletxhr.com/

	/*
 	JSON-P OVERVIEW:
 	jXHR is less than 1.3k minimized (and ~690 bytes gzip'd), and has no dependencies. 
 	jXHR is a clone-variant of the XMLHttpRequest object API, meaning it is (for the most part) 
 	API compatible. Some properties are not supported, such as responseText/XML/Body, and the 
 	request/response header functions are no-op's. Also, only "GET" is supported for the 'method' 
 	parameter of open(). And jXHR currently ignores any 'data' value passed to the send() function. 
 	All data you wish to send must be manually serialized into the 'url' parameter of open(). 
 	
 	USAGE:
 	
 	function handleError(msg,url) {alert(msg);}

	var x1 = new jXHR();
 	x1.onerror = handleError;
	x1.onreadystatechange = function(data){   //handle json-p callbacks using onreadystatechange
		if (x1.readyState === 4) {
			alert("Success:"+data.source);
		}
	};
	x1.open("GET","jsonme.php?callback=?&data="+encodeURIComponent(JSON.stringify({data:"my value 1"}))+"&_="+Math.random());
	x1.send();
	*/

; (function(global){
	var SETTIMEOUT = global.setTimeout, // for better compression
		doc = global.document,
		callback_counter = 0;
		
	global.jXHR = function() {
		var script_url,
			script_loaded,
			jsonp_callback,
			scriptElem,
			publicAPI = null;
			
		function removeScript() { try { scriptElem.parentNode.removeChild(scriptElem); } catch (err) { } }
			
		function reset() {
			script_loaded = false;
			script_url = "";
			removeScript();
			scriptElem = null;
			fireReadyStateChange(0);
		}
		
		function ThrowError(msg) {
			try { publicAPI.onerror.call(publicAPI,msg,script_url); } catch (err) { throw new Error(msg); }
		}

		function handleScriptLoad() {
			if ((this.readyState && this.readyState!=="complete" && this.readyState!=="loaded") || script_loaded) { return; }
			this.onload = this.onreadystatechange = null; // prevent memory leak
			script_loaded = true;
			if (publicAPI.readyState !== 4) {
				ThrowError("Script failed to load [" + script_url + "].");
			};
			removeScript();
		}
		
		function fireReadyStateChange(rs,args) {
			args = args || [];
			publicAPI.readyState = rs;
			if (typeof publicAPI.onreadystatechange === "function") {
				publicAPI.onreadystatechange.apply(publicAPI, args);
			}
		}
				
		publicAPI = {
			onerror:null,
			onreadystatechange:null,
			readyState:0,
			open:function(method,url){
				reset();
				internal_callback = "cb"+(callback_counter++);
				(function(icb){
					global.jXHR[icb] = function() {
						try { fireReadyStateChange.call(publicAPI,4,arguments); } 
						catch(err) { 
							publicAPI.readyState = -1;
							ThrowError("Script failed to run ["+script_url+"]."); 
						}
						global.jXHR[icb] = null;
					};
				})(internal_callback);
				script_url = url.replace(/=\?/,"=jXHR."+internal_callback);
				fireReadyStateChange(1);
			},
			send:function(){
				SETTIMEOUT(function(){
					scriptElem = doc.createElement("script");
					scriptElem.setAttribute("type","text/javascript");
					scriptElem.onload = scriptElem.onreadystatechange = function(){handleScriptLoad.call(scriptElem);};
					scriptElem.setAttribute("src",script_url);
					doc.getElementsByTagName("head")[0].appendChild(scriptElem);
				},0);
				fireReadyStateChange(2);
			},
			setRequestHeader:function(){}, // noop
			getResponseHeader:function(){return "";}, // basically noop
			getAllResponseHeaders:function(){return [];} // ditto
		};

		reset();
		
		return publicAPI;
	};
})(window);

namespace("http.JSONPRequest", jXHR);
